package com.nc.delivery_project.server.dao;

import com.nc.delivery_project.server.db_util.QueryManager;
import com.nc.delivery_project.server.util.CRUD;

import java.sql.*;
import java.util.List;

/**
 * Created by miroshnik on 06.12.2014.
 */
public abstract class AbstractDao<T> implements CRUD<T> {

    protected static final String SELECT_ALL = "select_all";
    protected static final String SELECT_BY_ID = "select_by_id";
    protected static final String UPDATE = "update";
    protected static final String INSERT = "insert";
    protected static final String DELETE = "delete";
    public static final int DEFAULT_INDEX_OF_RETURNING_PARAM = 2;

    protected Connection connection;
    protected QueryManager queryManager;
    protected String entityName;

    private AbstractDao() {
        queryManager = QueryManager.getInstance();
    }

    public AbstractDao(String entityName) {
        this();
        this.entityName = entityName;
    }

    public AbstractDao(Connection connection) throws SQLException {
        this();
        this.connection = connection;
    }

    public AbstractDao(Connection connection, String entityName) throws SQLException {
        this(connection);
        this.entityName = entityName;
    }

    @Override
    public T read(int id) throws SQLException {
        try (PreparedStatement statement = connection.prepareStatement(getSelectByIdQuery())) {
            statement.setInt(1, id);
            ResultSet rs = statement.executeQuery();
            List<T> result = parseResultSet(rs);
            
            return result.size() > 0 ? result.get(0) : null;
        }
    }

    @Override
    public List<T> read() throws SQLException {
        try (PreparedStatement statement = connection.prepareStatement(getSelectQuery())) {
            ResultSet rs = statement.executeQuery();
            return parseResultSet(rs);
        }
    }

    @Override
    public int create(T obj) throws SQLException {
        return create(obj, DEFAULT_INDEX_OF_RETURNING_PARAM);
    }

    @Override
    public void delete(int id) throws SQLException {
        try (PreparedStatement statement = connection.prepareStatement(getDeleteQuery())) {
            statement.setInt(1, id);
            statement.executeUpdate();
        }
    }

    @Override
    public void update(T obj) throws SQLException {
        try (CallableStatement statement = connection.prepareCall(getUpdateQuery())) {
            prepareUpdateStatement(statement, obj);
            statement.executeUpdate();
        }
    }

    public Connection getConnection() {
        return connection;
    }

    public void setConnection(Connection connection) {
        this.connection = connection;
    }
    
    protected int create(T obj, int indexOfReturningParam) throws SQLException {
        try (CallableStatement statement = connection.prepareCall(getInsertQuery())) {
            prepareInsertStatement(statement, obj);
            statement.executeUpdate();

            return statement.getInt(indexOfReturningParam);
        }
    }

    protected String getSelectQuery() {
        return queryManager.getQuery(entityName, SELECT_ALL);        
    }

    protected String getSelectByIdQuery() {
        return queryManager.getQuery(entityName, SELECT_BY_ID);        
    }

    protected String getInsertQuery() {
        return queryManager.getQuery(entityName, INSERT);
    }

    protected String getUpdateQuery() {
        return queryManager.getQuery(entityName, UPDATE);
    }

    protected String getDeleteQuery() {
        return queryManager.getQuery(entityName, DELETE);
    }

    protected abstract void prepareInsertStatement(CallableStatement statement, T object) throws SQLException;

    protected abstract void prepareUpdateStatement(CallableStatement statement, T object) throws SQLException;

    protected abstract List<T> parseResultSet(ResultSet rs) throws SQLException;
}
